﻿using System;
using System.Collections.Generic;
using System.Text;
using Gumini.JulaDB.Data;
using Gumini.JulaDB.Engine;
using Gumini.JulaDB.Interfaces;

namespace Gumini.JulaDB.Planner
{
    /// <summary>
    /// An abstract base class for all execution plan nodes.
    /// </summary>
    internal abstract class PlanNode
    {
        protected PlanNode(params PlanNode[] childen)
        {
            this.Children = new List<PlanNode>(childen);
        }

        public List<PlanNode> Children { get; private set; }

        public IHeading<TemporaryColumn> Heading
        {
            get
            {
                if (_columns == null)
                    _columns = GetColumns();
                return _columns;
            }
        }
        private IHeading<TemporaryColumn> _columns;

        public List<Row> Rows
        {
            get
            {
                if (_rows == null)
                    _rows = Execute();
                return _rows;
            }
        }
        private List<Row> _rows;

        public abstract IHeading<TemporaryColumn> GetColumns();

        public abstract List<Row> Execute();

        public IEnumerable<T> GetNodesByType<T>()
            where T : PlanNode
        {
            if (this is T)
                return new[] { (T)this };

            var result = new List<T>();
            foreach (var child in this.Children)
                result.AddRange(child.GetNodesByType<T>());

            return result;
        }

        public bool ContainsNode(PlanNode node)
        {
            if (this == node)
                return true;
            foreach (var child in this.Children)
                if (child.ContainsNode(node))
                    return true;
            return false;
        }

        protected abstract string GetNodeDescription();

        protected string ToString(int indentLevel)
        {
            var sb = new StringBuilder();
            sb.Append(new String(' ', indentLevel * 2));
            sb.AppendLine(this.GetNodeDescription());
            foreach (var child in this.Children)
            {
                sb.Append(child.ToString(indentLevel + 1));
            }
            return sb.ToString();
        }

        public override string ToString()
        {
            return this.ToString(0);
        }
    }
}
